//
//  DisplayPagingLayout.swift
//  Media
//
//  Created by han on 2021/6/8.
//

import UIKit

class DisplayPagingLayout: UICollectionViewFlowLayout {
    var mininumScaleFactory: CGFloat = 0.9
    var maxinumScaleFactory: CGFloat = 1.0
    var mininumAlphaFactory: CGFloat = 1.0
    var maxinumAlphaFactory: CGFloat = 1.0
    var scalingOffset: CGFloat = 200
    
    private var finalItemSize: CGSize?
    private var lastSize: CGSize = CGSize.zero
    
    override init() {
        super.init()
        commonInit()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    private func commonInit() {
        scrollDirection = .horizontal
        scalingOffset = collectionView?.bounds.width ?? 400/2
    }
}

extension DisplayPagingLayout {
    override func prepare() {
        super.prepare()
    }
    
    override func invalidateLayout() {
        super.invalidateLayout()
        guard let collectionView = collectionView else { return }
        
        if lastSize != collectionView.bounds.size {
            configInsert()
            lastSize = collectionView.bounds.size
        }
    }
    
    override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
        return true
    }
    
    override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
        guard let collectionView = collectionView else { return proposedContentOffset }
        
        let proposedRect = CGRect(x: proposedContentOffset.x, y: proposedContentOffset.y, width: collectionView.bounds.width, height: collectionView.bounds.height)
        guard let attrs = layoutAttributesForElements(in: proposedRect) else { return proposedContentOffset }
        
        let proposedContentOffsetCenterX = proposedContentOffset.x + collectionView.bounds.width / 2
        
        var candidate: UICollectionViewLayoutAttributes?
        
        for attributes in attrs {
            
            if attributes.representedElementCategory != .cell {
                continue
            }
            
            if candidate == nil {
                candidate = attributes
                continue
            }
            
            if abs(proposedContentOffsetCenterX - attributes.center.x) < abs(proposedContentOffsetCenterX - candidate!.center.x) {
                candidate = attributes
            }
        }
        
        guard let aAttributes = candidate else { return proposedContentOffset }
        
        var offsetX = aAttributes.center.x - collectionView.bounds.width/2
        
        let offset = offsetX - collectionView.contentOffset.x
        
        if (velocity.x > 0 && offset < 0) || (velocity.x < 0 && offset > 0) {
            let itemWidth = itemSize.width + minimumLineSpacing
            offsetX += velocity.x > 0 ? itemWidth : -itemWidth
        }
        return CGPoint(x: offsetX, y: proposedContentOffset.y)
    }
    
    override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
        guard let collectionView = collectionView, let superAttributes = super.layoutAttributesForElements(in: rect)  else { return super.layoutAttributesForElements(in: rect) }
        
        guard case let newAttributes as [UICollectionViewLayoutAttributes] = NSArray(array: superAttributes, copyItems: true) else { return superAttributes }
        
        let visibleRect = CGRect(x: collectionView.contentOffset.x, y: collectionView.contentOffset.y, width: collectionView.bounds.width, height: collectionView.bounds.height)
        let visibleCenterX = visibleRect.midX
        
        newAttributes.forEach {
            let absDistanceFromCenter = min(abs(visibleCenterX - $0.center.x), scalingOffset)
            let scale = absDistanceFromCenter * (self.mininumScaleFactory - 1) / scalingOffset + 1
            $0.transform3D = CATransform3DScale(CATransform3DIdentity, scale, scale, 1)
            
            let alpha = absDistanceFromCenter * (self.mininumAlphaFactory - 1) / scalingOffset + 1
            $0.alpha = alpha
        }
        
        return newAttributes
    }
}


extension DisplayPagingLayout {
    private func configInsert() {
        guard let collectionView = collectionView else { return }
        let insert = collectionView.bounds.width/2 - itemSize.width/2
        collectionView.contentInset = UIEdgeInsets(top: 0, left: insert, bottom: 0, right: insert)
        collectionView.contentOffset = CGPoint(x: -insert, y: 0)
    }
}
